package com.xjh.him.core.netty;

import com.xjh.him.core.netty.codec.LengthFieldDecoder;
import com.xjh.him.core.netty.codec.MsgDecoder;
import com.xjh.him.core.netty.codec.MsgEncoder;
import com.xjh.him.core.netty.codec.Packet;
import com.xjh.him.core.netty.handler.BaseHandler;
import com.xuexiang.xutil.common.logger.Logger;

import java.net.InetSocketAddress;
import java.util.concurrent.CountDownLatch;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;


public class NettyClient {
    private InetSocketAddress address;
    private NioEventLoopGroup workerGroup;
    private Bootstrap bootstrap;
    private ChannelInboundHandler respHandler;
    private ChannelFuture future;

    private volatile static NettyClient instance = null;

    /**
     * 获取单例 nettyClient
     * @param address
     * @param port
     * @return
     */
    public static NettyClient newInstance(String address, int port) {
        if (instance == null) {
            synchronized (NettyClient.class) {
                if (instance == null) {
                    InetSocketAddress socketAddress = new InetSocketAddress(
                            address, port
                    );
                    instance = new NettyClient(socketAddress, new BaseHandler());
                }
            }
        }
        return instance;
    }

    public static NettyClient getInstance() {
        return instance;
    }

    public NettyClient(InetSocketAddress address, ChannelInboundHandler respHandler) {
        this.address = address;
        this.respHandler = respHandler;
        workerGroup = new NioEventLoopGroup();
        bootstrap = new Bootstrap();
    }

    public void connect() {
        if (future == null) {
            bootstrap.group(workerGroup)
                    .channel(Epoll.isAvailable() ? EpollSocketChannel.class :
                            NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) {
                            ChannelPipeline pipeline = channel.pipeline();

                            pipeline.addLast("Client LengthFieldDecoder", new LengthFieldDecoder());
                            pipeline.addLast("Client Decoder", new MsgDecoder());
                            pipeline.addLast("Client ResponseHandler", respHandler);
                            pipeline.addLast("Client Encoder", new MsgEncoder());
                        }
                    });
            future = bootstrap.connect(address);

            //同步方式连接
            final CountDownLatch latch = new CountDownLatch(1);
            future.addListener((ChannelFutureListener) f -> {
                if (f.isSuccess()) {
                    Logger.i(String.format("client connect %s success.", address));
                }else {
                    f.channel().pipeline().fireChannelInactive();
                    Logger.i(String.format("client connect %s failed, err: %s", address, f.cause().getMessage()));
                }
                latch.countDown();
            });
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public ChannelFuture sendMsg(Packet packet) {
        return future.channel().writeAndFlush(packet);
    }

    public void disconnect() {
        future.channel().close();

        address = null;
        workerGroup = null;
        bootstrap = null;
        respHandler = null;
        future = null;
        instance = null;
    }
}
